package com.cs410.collabwriting.main;

import java.sql.Date;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import com.cs410.collabwriting.tools.DatabaseManager;
import com.cs410.collabwriting.tools.LogManager;
import com.cs410.collabwriting.tools.queryTools.DatabaseTable;
import com.cs410.collabwriting.tools.queryTools.QueryBuilder;
import com.cs410.collabwriting.tools.queryTools.QueryCondition;
import com.cs410.collabwriting.tools.queryTools.QueryType;
import com.cs410.collabwriting.tools.queryTools.tables.POST_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.STORY_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.SUBSCRIBED_STORY_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.SUBSCRIBED_USER_TABLE;
import com.cs410.collabwriting.tools.queryTools.tables.USER_TABLE;

public class User implements DatabaseObjects {

	// Fairly Static Variables
	private String m_userID = null;
	private String m_password = null;
	private String m_lastName = null;
	private String m_firstName = null;
	private String m_biography = null;
	private String m_location = null;
	private String m_gender = null;
	private String m_dateCreated = null;
	private String m_lastUpdate = null;
	private String m_emailAddress = null;
	private String m_activateKey = null;
		
	private Date m_activity = null;
	
	public User( String userID ) {
		m_userID = userID;
		m_activity = new Date( Calendar.getInstance().getTimeInMillis() );
	}
	
	public boolean isActive() {
		Date current = new Date( Calendar.getInstance().getTimeInMillis() );
		long lastActivity = m_activity.getTime();
		long currentActivity = current.getTime();
		if( currentActivity - lastActivity > 600000 ) {
			return false;
		}
		m_activity = current;
		return true;
	}
	
	public boolean setEmailAddress( String emailAddress) {
		int set = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.EMAIL)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues( emailAddress )
			.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, m_userID ) );
		return set > 0 ? true : false;
	}
	
	public String getEmailAddress() {
		if( m_emailAddress == null ) {
			m_emailAddress = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.EMAIL)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, m_userID) );
		}
		return m_emailAddress;
	}
	public String getActivateKey() {
		if( m_activateKey == null ) {
			m_activateKey = DatabaseManager.executeExactQuery(
					new QueryBuilder(QueryType.SELECTION)
					.addColumns(USER_TABLE.ACTIVATEKEY)
					.addTables(DatabaseTable.USER_TABLE)
					.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, m_userID) );
		}
		return m_activateKey;
	}
	
	public boolean setActivateKey(String emailAddress) {
		double random = Math.random() * 1000000000 + 1;
		int emailHash = emailAddress.hashCode();
		String activateKey = Double.toHexString( random * emailHash ).substring(5);
		int set = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.ACTIVATEKEY)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues( activateKey )
			.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, m_userID ) );
		return set > 0 ? true : false;
	}
	
	public boolean activateUser(String userID, String key) {
		String successkey = "0";
		String actualkey = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.ACTIVATEKEY)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(userID) )
				.value() );
		if ( actualkey != null && !actualkey.isEmpty() &&
				actualkey.compareToIgnoreCase( key ) == 0 ) {
			int set = DatabaseManager.executeUpdate(
					new QueryBuilder(QueryType.UPDATE)
					.addColumns(USER_TABLE.ACTIVATEKEY)
					.addTables(DatabaseTable.USER_TABLE)
					.addValues( successkey )
					.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, userID ) );
			getActivateKey();
			return set > 0 ? true : false;
		}
		return false;
	}
		
	public String getPassword() {
		if( m_password == null ) {
			m_password = DatabaseManager.executeExactQuery(
					new QueryBuilder(QueryType.SELECTION)
					.addColumns(USER_TABLE.PASSWORD)
					.addTables(DatabaseTable.USER_TABLE)
					.addConditions(QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, m_userID) );
		}
		return m_password;
	}
	
	public boolean subscribeToStory( int storyID ) {
		int subscribed = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.INSERT)
			.addColumns(SUBSCRIBED_STORY_TABLE.USERID, SUBSCRIBED_STORY_TABLE.STORYID)
			.addTables( DatabaseTable.SUBSCRIBED_STORY_TABLE )
			.addValues( m_userID ,  storyID + "")
			.value() );				
		return subscribed > 0 ? true : false;
	}
	
	public boolean isSubscribedToStory( int storyID ) {
		String count = DatabaseManager.executeExactQuery(
			new QueryBuilder(QueryType.SELECTION)
			.addColumns("COUNT(*)")
			.addTables( DatabaseTable.SUBSCRIBED_STORY_TABLE )
			.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.USERID , QueryCondition.EQ, m_userID ) 
			.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.STORYID , QueryCondition.EQ, storyID )
		);
		if( count != null && Integer.parseInt( count ) > 0 ) {
			return true;
		}
		return false;
	}
	
	public boolean isSubscribedToWriter( String writerID ) {
		String count = DatabaseManager.executeExactQuery(
			new QueryBuilder(QueryType.SELECTION)
			.addColumns("COUNT(*)")
			.addTables( DatabaseTable.SUBSCRIBED_USER_TABLE )
			.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.USERID , QueryCondition.EQ, m_userID ) 
			.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.FAVUSERID , QueryCondition.EQ, writerID )
		);
		if( count != null && Integer.parseInt( count ) > 0 ) {
			return true;
		}
		return false;
	}
	
	public boolean subscribeToWriter( String writerID ) {
		int subscribed = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.INSERT)
			.addColumns(SUBSCRIBED_USER_TABLE.USERID, SUBSCRIBED_USER_TABLE.FAVUSERID)
			.addTables(DatabaseTable.SUBSCRIBED_USER_TABLE)
			.addValues( QueryBuilder.toSQLChar(m_userID),  QueryBuilder.toSQLChar(writerID) )
			.value() );
		return subscribed > 0 ? true : false;
	}
	
	public boolean setPassword( String newPassword ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.PASSWORD)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(newPassword))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}
	
	public boolean setLastName( String lastName ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.LASTNAME)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(lastName))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}
	
	public boolean setFirstName( String firstName ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.FIRSTNAME)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(firstName))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}
	
	public boolean setBiography( String biography ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.BIOGRAPHY)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(biography))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}
	
	public boolean setLocation( String location ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.LOCATION)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(location))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}
	
	public boolean setGender( String gender ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.GENDER)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues(QueryBuilder.toSQLChar(gender))
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		return updated > 0 ? true: false;
	}

	public boolean setLastUpdated( long date ) {
		int updated = DatabaseManager.executeUpdate(
			new QueryBuilder(QueryType.UPDATE)
			.addColumns(USER_TABLE.LASTUPDATE)
			.addTables(DatabaseTable.USER_TABLE)
			.addValues( new Timestamp(date).toString() )
			.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
			.value() );
		if( updated > 0 ) {
			m_lastUpdate = new Timestamp(date).toString();
			return true;
		}
		return false;
	}
	
	public String getUserID() {
		return m_userID;
	}
	
	public String getFirstName() {
		if( m_firstName == null ) {
			m_firstName = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.FIRSTNAME)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_firstName;
	}
	
	public String getLastName() {
		if( m_lastName == null ) {
			m_lastName = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.LASTNAME)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_lastName;
	}
	
	public String getBiography() {
		if( m_biography == null ) {
			m_biography = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.BIOGRAPHY)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_biography;
	}
	
	public String getLocation() {
		if( m_location == null ) {
			m_location = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.LOCATION)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_location;
	}
	
	public String getGender() {
		if( m_gender == null ) {
			m_gender = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.GENDER)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_gender;
	}
	
	public String getDateCreated() {
		if( m_dateCreated == null ) {
			m_dateCreated = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.DATECREATED)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ , QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_dateCreated;
	}
	
	public String getLastUpdate() {
		if( m_lastUpdate == null ) {
			m_lastUpdate = DatabaseManager.executeExactQuery(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns(USER_TABLE.LASTUPDATE)
				.addTables(DatabaseTable.USER_TABLE)
				.addConditions( QueryCondition.AND, USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar(m_userID) )
				.value() );
		}
		return m_lastUpdate;
	}
	
	public ArrayList<String[]> getSubscriptionUpdates() {
		ArrayList<String[]> subUpdates = DatabaseManager.executeQueryWithColumnNames(
				new QueryBuilder(QueryType.SELECTION)
				.addColumns( DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.FAVUSERID   )
				.addColumns( DatabaseTable.POST_TABLE + "." + POST_TABLE.TIMESTAMP , DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYTITLE )
				.addColumns( DatabaseTable.POST_TABLE + "." + POST_TABLE.POST )
				.addTables(DatabaseTable.USER_TABLE, DatabaseTable.SUBSCRIBED_USER_TABLE, DatabaseTable.POST_TABLE, DatabaseTable.STORY_TABLE )
				.addConditions( QueryCondition.AND, DatabaseTable.USER_TABLE + "." + USER_TABLE.USERID, QueryCondition.EQ, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.USERID )
				.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_USER_TABLE + "." + SUBSCRIBED_USER_TABLE.FAVUSERID, QueryCondition.EQ, DatabaseTable.POST_TABLE + "." + POST_TABLE.USERID )
				.addConditions( QueryCondition.AND, DatabaseTable.USER_TABLE + "." + USER_TABLE.USERID, QueryCondition.EQ, QueryBuilder.toSQLChar( m_userID ) )
				.addConditions( QueryCondition.AND, DatabaseTable.POST_TABLE + "." + POST_TABLE.STORYID, QueryCondition.EQ, DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID )
				.addAdditionalCriteria("ORDER BY POST_TABLE.TIMESTAMP ASC")
				.addAdditionalCriteria("LIMIT 10")
				.value() );
		ArrayList<String[]> storyUpdates = DatabaseManager.executeQueryWithColumnNames(
				new QueryBuilder( QueryType.SELECTION)
				.addColumns( DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID, DatabaseTable.POST_TABLE + "." + POST_TABLE.USERID   )
				.addColumns( DatabaseTable.POST_TABLE + "." + POST_TABLE.TIMESTAMP , DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYTITLE )
				.addColumns( DatabaseTable.POST_TABLE + "." + POST_TABLE.POST )
				.addTables(DatabaseTable.SUBSCRIBED_STORY_TABLE, DatabaseTable.POST_TABLE, DatabaseTable.STORY_TABLE )
				.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.USERID  , QueryCondition.EQ , m_userID )
				.addConditions( QueryCondition.AND, DatabaseTable.SUBSCRIBED_STORY_TABLE + "." + SUBSCRIBED_STORY_TABLE.STORYID , QueryCondition.EQ , DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID )
				.addConditions( QueryCondition.AND, DatabaseTable.POST_TABLE + "." + POST_TABLE.STORYID, QueryCondition.EQ, DatabaseTable.STORY_TABLE + "." + STORY_TABLE.STORYID )
				.addAdditionalCriteria("ORDER BY POST_TABLE.TIMESTAMP ASC")
				.addAdditionalCriteria("LIMIT 10")
				.value() );
		ArrayList<String[]> merged = new ArrayList<String[]>();
		merged.add( new String[] {"storyid","favuserid","timestamp","storytitle","post"} );
		int i = 1, j = 1;
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
		while( i < subUpdates.size() || j < storyUpdates.size() ) {
			String[] toAdd = new String[5];
			if( i < subUpdates.size() && j < storyUpdates.size() ) {
				String[] rowOne = subUpdates.get( i );
				String[] rowTwo = storyUpdates.get( j );	
				try {
					java.util.Date one = df.parse( rowOne[2] );
					java.util.Date two = df.parse( rowTwo[2] );
					if( one.compareTo( two ) <= 0 ) {
						toAdd[0] = rowOne[0];
						toAdd[1] = rowOne[1];
						toAdd[2] = rowOne[2];
						toAdd[3] = rowOne[3];
						toAdd[4] = rowOne[4];
						i++;
					} else {
						toAdd[0] = rowTwo[0];
						toAdd[1] = rowTwo[1];
						toAdd[2] = rowTwo[2];
						toAdd[3] = rowTwo[3];
						toAdd[4] = rowTwo[4];
						j++;
					}
				} catch (ParseException e ) {
					LogManager.logError("[USER] -- DATE PARSE EXCEPTION : " + e.getMessage() );
				}
			} else {
				if( i < subUpdates.size() ) {
					String[] rowOne = subUpdates.get( i++ );
					toAdd[0] = rowOne[0];
					toAdd[1] = rowOne[1];
					toAdd[2] = rowOne[2];
					toAdd[3] = rowOne[3];
					toAdd[4] = rowOne[4];
				} else {
					String[] rowTwo = storyUpdates.get( j++ );
					toAdd[0] = rowTwo[0];
					toAdd[1] = rowTwo[1];
					toAdd[2] = rowTwo[2];
					toAdd[3] = rowTwo[3];
					toAdd[4] = rowTwo[4];
				}
			}
			merged.add( toAdd );
		}
		setLastUpdated(Calendar.getInstance().getTimeInMillis() );
		return merged;
	}
	
	@Override
	public void update() {
		m_lastName = null;
		m_firstName = null;
		m_biography = null;
		m_location = null;
		m_gender = null;
		m_dateCreated = null;
		m_lastUpdate = null;
		m_emailAddress = null;
	}

}
